/*
 * Copyright (c) 2024 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#ifndef SERVICE_BUDDY_H
#define SERVICE_BUDDY_H

#include <plat/config.h>
#include <tools/list.h>
#include <stdlib/stdint.h>
#include <tools/macros.h>

#define CONFIG_DYN_INIT_BUDDY 1

typedef struct p__ppage {
    unsigned int flags : 3;
    unsigned int slab_head : 10;
    unsigned int slab_size_ll : 9;
    unsigned int slab_used : 10;

    unsigned int secindex : 4;
    unsigned int size : 23;
    unsigned int lbsize : 5;

    void   *base;
    slist_t b_next;
    slist_t s_next;
    slist_t slab_next;

} ppage_t;

#define PG_LOCKED 0x1

typedef struct psection {
    void        *begin;
    unsigned int size : 31;
    unsigned int ext : 1;
    ppage_t     *pages;

    slist_t ltop;
    slist_t ltail;

} psection_t;

typedef struct p__pbuddy {
    slist_t     ltop[CONFIG_PAGE_MAX_INDEX + 1];
    slist_t     ltail[CONFIG_PAGE_MAX_INDEX + 1];
    uint16_t    seccount;
    psection_t *secs;
} pbuddy_t;

#define BTOP_POP(buddy, page)                                            \
    do {                                                                 \
        slist_t *fpage = &buddy->ltop[page->lbsize];                     \
        while (*fpage && *(slist_t *)(*fpage) != page->b_next)           \
            fpage = *(slist_t *)fpage;                                   \
        assert(container_of(slist_pop(fpage), ppage_t, b_next) == page); \
    } while (0)

#define BUDDY_SIZE(secs, pages) (sizeof(pbuddy_t) + sizeof(psection_t) * (secs) + sizeof(ppage_t) * (pages))

pbuddy_t *pbuddy_init(void **block, unsigned int *pages, unsigned int seccount);
int       pbuddy_alloc_page(pbuddy_t *buddy, unsigned int pages, ppage_t **page);
void      pbuddy_free_page(pbuddy_t *buddy, ppage_t *page);
int       pbuddy_find_page(pbuddy_t *buddy, void *ptr, ppage_t **page);

#endif
